package priv.conceit.sc.hoxton.common.web.aop.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import lombok.var;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import priv.conceit.sc.hoxton.common.entity.common.web.dto.ReqLogDTO;
import priv.conceit.sc.hoxton.common.util.DateTimeUtil;
import priv.conceit.sc.hoxton.common.web.security.WebUserDetail;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * sc-hoxton 日志收集记录
 *
 * @author conceit
 * @since 1.0.0, 2020/12/10
 */
@Slf4j
@Component
@Aspect
public class ReqLogAspect {

    /**
     * 匹配方法
     */
    private static final String expression= "(execution(public * priv.conceit.sc.hoxton..controller.*Controller.*(..)))";

    /**
     * 切入点
     */
    @Pointcut(expression)
    public void pointCut(){}


    /**
     * 环绕执行
     * @param pjp 连接点
     * @return Object
     * @throws Throwable
     */
    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long startTime=System.currentTimeMillis();
        Object result=null;
        try{
            result=pjp.proceed();
        }catch (Throwable e){
            throw e;
        }finally {
            long endTime=System.currentTimeMillis();
            record(startTime,endTime,pjp,result);
        }
        return result;
    }

    /**
     * record 执行日志记录
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param pjp 结束时间
     * @param pjp 结束时间
     * @return void
     * @author conceit
     * @date 2020/12/11 10:31
     */
    private void record(long startTime,long endTime,ProceedingJoinPoint pjp,Object result){
        ServletRequestAttributes attributes=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if(Objects.isNull(attributes)){
            return;
        }
        HttpServletRequest request=attributes.getRequest();
        if(Objects.isNull(request)) {
            return ;
        }
        ReqLogDTO reqLogDTO=new ReqLogDTO();

        reqLogDTO.setStartTime(DateTimeUtil.format(startTime,DateTimeUtil.FormatEnum.yyyy年MM月dd日空格HH时mm分ss秒SSS毫秒.value));
        reqLogDTO.setEndTime(DateTimeUtil.format(endTime,DateTimeUtil.FormatEnum.yyyy年MM月dd日空格HH时mm分ss秒SSS毫秒.value));
        long executeDuration=endTime-startTime;
        reqLogDTO.setExecuteDuration(executeDuration + "毫秒");

        reqLogDTO.setIp(request.getRemoteAddr());
        reqLogDTO.setHttpMethod(request.getMethod());
        reqLogDTO.setUri(request.getRequestURL().toString());



        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();
        StringBuffer reqClassAndMethod=new StringBuffer();
        reqClassAndMethod.append(method.getDeclaringClass().getName());
        reqClassAndMethod.append(".");
        reqClassAndMethod.append(method.getName());
        reqLogDTO.setReqClassAndMethod(reqClassAndMethod.toString());

        List<ReqLogDTO.ReqArg> reqArgs=new ArrayList<>();

        for(int i=0;i<pjp.getArgs().length;i++){

            Class typeClazz=method.getParameterTypes()[i];
            String name=((MethodSignature) pjp.getSignature()).getParameterNames()[i];
            String type=((MethodSignature) pjp.getSignature()).getParameterTypes()[i].getSimpleName();

            Annotation[] annos=method.getParameterAnnotations()[i];
            String  anno=Arrays.stream(annos).map(Annotation::annotationType).map(Class::getSimpleName).collect(Collectors.joining(","));
            ReqLogDTO.ReqArg reqArg=new ReqLogDTO().new ReqArg();
//            if(InputStreamSource.class.isInstance(typeClazz)){
//                reqArg.setValue("文件传输类型无法预览参数值！跳过处理！");
//            }else{
//                reqArg.setValue(pjp.getArgs()[i]);
//            }
            boolean flag=true;
            Object value=new String("参数值包含文件或流！无法序列化！");
            try{
                value = new ObjectMapper().writeValueAsString(pjp.getArgs()[i]);
            }catch (Exception e){
                flag=false;
            }
            if(flag){
                reqArg.setValue(pjp.getArgs()[i]);
            }else {
                reqArg.setValue(value);
            }

            reqArg.setAnno(anno);
            reqArg.setName(name);
            reqArg.setType(type);
            reqArgs.add(reqArg);
        }
        reqLogDTO.setReqArgs(reqArgs);


        val securityContext = SecurityContextHolder.getContext();
        val authentication = securityContext.getAuthentication();
		if(Objects.nonNull(authentication)){
			var principal= (WebUserDetail)authentication.getPrincipal();
			if(Objects.nonNull(principal)){
				reqLogDTO.setUserId(principal.getCurrentUserInfoDTO().getUser().getId());
				reqLogDTO.setUsername(principal.getCurrentUserInfoDTO().getUser().getName());
			}
		}

        ObjectMapper mapper = new ObjectMapper();
        String json=new String();
        try {
             json = mapper.writeValueAsString(reqLogDTO);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        log.info("请求执行结果->{}",json);
    }

}
